%%%-------------------------------------------------------------------
%%% @author wukai
%%% @copyright (C) 2017, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 20. 十二月 2017 20:22
%%%-------------------------------------------------------------------
-module(session_server).
-author("wukai").

-behaviour(gen_server).
-record(session, {
  client, %%客户端标识符
  willmsg,
  pid,
  clean_session,
  status, %%状态 offline,online
  create_time
  %%遗嘱主题
}).
%% API
-export([start_link/0]).
-export([cre_tab/0]).
-export([send_offline/2]).
-export([send_online/4]).
-export([publish/2]).

%% gen_server callbacks
-export([init/1,
  handle_call/3,
  handle_cast/2,
  handle_info/2,
  terminate/2,
  code_change/3]).
-define(SESSION_TAB, ets_session).
-define(SERVER, ?MODULE).

-record(state, {}).
start_link() ->
  gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

init([]) ->
  {ok, #state{}}.
handle_call(_Request, _From, State) ->
  {reply, ok, State}.
handle_cast({online, Client, Pid, Clean, WillMsg}, State) ->
  create(Client, Pid, Clean, WillMsg), {noreply, State};
handle_cast({offline, Client, Clean}, State) ->
  update(Client, Clean), {noreply, State};
handle_cast(_Request, State) ->
  {noreply, State}.
handle_info(_Info, State) ->
  {noreply, State}.

terminate(_Reason, _State) ->
  ok.
code_change(_OldVsn, State, _Extra) ->
  {ok, State}.

%%创建session表
cre_tab() ->
  ets:new(?SESSION_TAB, [set, public, named_table,
    {write_concurrency, true}, {read_concurrency, true},
    {keypos, #session.client}]).

create(Client, NewPid, Clean, WillMsg) ->
  Session = #session{client = Client, %%客户端
    pid = NewPid,%%进程id,
    clean_session = Clean,
    willmsg = WillMsg,
    status = online, %%上线
    create_time = time_utils:timestamp() %%创建时间
  },
  %%是否存在元素

  try
    Pid = ets:lookup_element(?SESSION_TAB, Client, 4),
    case is_process_alive(Pid) of
      true -> Pid ! {reconn, NewPid};
      false -> ok
    end
  catch
    _:_ -> log:log("new session ~p", [Client])
  end,

  ets:insert(?SESSION_TAB, Session).



update(Client, 0) -> ets:update_element(?SESSION_TAB, Client, {5, offline});
update(Client, 1) -> ets:delete(?SESSION_TAB, Client).


send_online(Client, Pid, Clean, WillMsg) ->
  log:log("client ~p online", [Client]),
  notify_status(Client, online),
  gen_server:cast(?MODULE, {online, Client, Pid, Clean, WillMsg}).

send_offline(Client, CleanSession) ->
  log:log("client ~p offline", [Client]),
  notify_status(Client, offline),
  gen_server:cast(?MODULE, {offline, Client, CleanSession}).

%%发送消息给设备
publish(Client, P) ->
  List = ets:lookup(?SESSION_TAB, Client),
  case length(List) of
    A when A > 0 -> lists:foreach(fun(E) ->
      dispatch_server:send_or_save(Client, P, E#session.pid) end, List);
    _ -> ok
  end.

notify_status(Client, Status) ->
  {UserName, ClientId} = Client,
  Time = erlang:integer_to_binary(time_utils:timestamp()),
  Msg = jsx:encode([{<<"user">>, list_to_binary(UserName)}, {<<"client_id">>, list_to_binary(ClientId)}, {<<"time">>, Time}]),
  Topic = case Status of
            offline ->
              <<"$sys/client/disconnect">>;
            online -> <<"$sys/client/connect">>
          end,
  Tlen = byte_size(Topic),
  P = publish_frame:wrap(Client, Topic, Tlen, Msg, 0, 0, 0),
  dispatch_server:send_dispatch(P).